home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programmer Power Tools
/
Programmer Power Tools.iso
/
c
/
make.c
< prev
next >
Wrap
Text File
|
1984-06-28
|
26KB
|
1,291 lines
\b readme
Last month I wrote a version of the Unix(tm) utility MAKE. It runs under
VAX/VMS and MSDOS 2.0. I am placing it in the public domain, and it is yours
for the asking. You may copy it, or give it away. You can make any changes
you like to it. All I ask is that you DO NOT TRY TO SELL IT.
Anyway, there is now a MAKE for MSDOS. It is free, and it works pretty well.
I'm giving it away because it might do the world some good. Who knows?
Caveat: this version of MAKE is NOT compatible with the Unix(tm) version.
Some differences are explained in the documentation. Most of the problem stems
from the fact that I've never had a chance to use the original version of MAKE,
and the documentation I've seen on it has been poor. My idea of what a make
program should do is almost certainly different from what you Unix(tm) hackers
are used to. Well, hell -- the software is worth what you paid for it. Have
fun.
In order to get MAKE running on your system, you need to:
1. Read the documentation file MAKE.MAN. (Yes, read the
directions.)
2. Edit the file MAKE.H to represent your system (VAX/VMS or
MSDOS 2.0.)
3. Recompile the source code by following the script file
CMAKE.COM (for VAX/VMS) or CMAKE.BAT (for MSDOS 2.0.)
VAX/VMS requires the DEC C compiler; MSDOS 2.0 requires
Lattice C (or another C compiler of comparable quality)
and the Macro Assembler.
4. Test out MAKE by running it on itself. (Make a backup
first!)
Good luck,
Landon Dyer (G.DYER @ SU-SCORE)
\e readme
\b make.man
MAKE(I) 3/10/84 MAKE(I)
NAME
MAKE - maintain multiple source files (VAX/VMS and MSDOS 2.0)
SYNOPSIS
MAKE [-N] [-A] [-F makefile] [name ...]
DESCRIPTION
MAKE is a utility inspired by the Unix(tm) command of the same
name. MAKE helps maintain programs that are constructed from
many files. MAKE processes a "makefile", a file which describes
how to build a program from its source files, and produces a
script file containing the commands necessary to recompile the
program.
Be careful: this MAKE is NOT compatible with Unix(tm) MAKE!
The 'N' option causes MAKE to print out the steps it would follow
in order to rebuild the program. The 'A' option tells MAKE to
assume that all files are obsolete, and that everything should be
recompiled. The 'F' option, followed by a filename, can be used
to specify a makefile other than the default one.
If no names are specified in the commandline, the first dependency
in the makefile is examined. Otherwise, the specified root names
are brought up to date.
The default makefiles are:
for VAX/VMS: MAKEFILE
[-]MAKEFILE
SYS$LOGIN:MAKEFILE
for MSDOS: MAKEFILE
..\MAKEFILE
If the first makefile cannot be found, MAKE attempts to use the
next one. If no makefile is ever found, MAKE prints a diagnostic
and aborts.
THE MAKEFILE
Comments begin with '!' and extend to the end of the line. A
'!' (or almost any other character) may be escaped with the escape
character (backslash (\) on VMS, backquote (`) on MSDOS). An escape
character may be typed by doubling it (\\ or ``). The standard
Unix escape codes are recognized (\n, \r, \t, \b, \f, `n, `r, `t,
`b and `f).
A makefile is a list of dependencies. A dependency consists of
a root name, a colon, and zero or more names of dependent files.
(The colon MUST be preceeded by whitespace.) For instance, in:
make.exe : make.obj parsedir.obj file.obj macro.obj mk.h
the file 'make.exe' depends on five other files. A root name
with an empty dependency, as in:
print :
is assumed NEVER up to date, and will always be recompiled.
The dependency list may be continued on successive lines:
bigfile.exe : one.obj two.obj three.obj four.obj
five.obj six.obj gronk.obj freeple.obj scuzzy.lnk
frog.txt greeble.out
Any number of 'method' lines may follow a dependency. Method lines
begin with an ascii tab. When a file is to be recompiled, MAKE
copies these method lines (minus the tab) to the script file.
For example, in:
make.exe : make.obj parsedir.obj file.obj macro.obj mk.h
$link make, parsedir, file, macro
$write sys$output "Just another version of MAKE ..."
$purge
the three lines following the dependency make up the method for
recompiling (or in this case, re-linking) the file 'make.exe'.
If the macro "~INIT" is defined, its text will appear first in the
script file. If the macro "~DEINIT" is defined, its text will
appear last in the script file. By defining these two macros, it
is possible to configure the shell enviroment:
~INIT = $set term/nowrap\n$on error then goto err_handler
~DEINIT = $set term/wrap\n$exit\$err_handler:\n
~DEINIT = #(~DEINIT)$type err.log\n$exit
will expand (in the script file) to:
$set term/nowrap
$on error then goto err_handler
.
.
$set term/wrap
$exit
$err_handler:
$type err.log
$exit
When a root's method is defined, the value of the macro "~BEFORE"
is prefixed to the method, and the value of the macro "~AFTER" is
appended to it.
Frequently one wants to maintain more than one program with a single
makefile. In this case, a "master dependency" can appear first in
the file:
allOfMyToolsAndHorribleHacks : cat peek poke.exe grunge
cat : cat.exe
cat.exe : ....
(stuff for CAT.EXE)
peek : peek.exe
peek.exe : (stuff for PEEK.EXE)
poke.exe : (stuff for POKE.EXE)
grunge : grunge.com
grunge.com : (stuff for grung)
In other words, make will bring everything up to date that is somehow
connected to the first dependency (its assumed that the incredibly
lengthy filename specified in this example won't actually exist).
MACROS
A macro is defined by a line of the form (the '=' MUST be surrounded
by whitespace):
<macro-name> = <macro-body>
A macro may be deleted by assigning an empty value to it. Macros
may be redefined, but old definitions stay around. If a macro is
redefined, and the redefinition is later deleted, the first definition
will take effect:
MAC = first ! MAC = "first"
MAC = second ! MAC = "second"
MAC = #(MAC) third ! MAC = "second third"
MAC = ! MAC = "second"
MAC = ! MAC = "first"
MAC = ! MAC has no definition
A macro may be referenced in two ways:
#<char> or #(macro-name)
The first way only works if the macro's name is a single character.
If the macro's name is longer than one character, it must be
enclosed in parenthesis. ['#' may be escaped by doubling it ("##".)]
For example, in:
G = mk.h mk1.h
OBJS = make.obj file.obj parsedir.obj macro.obj
BOTH = #(OBJS) #G
make.exe : #(OBJS) #G
make.exe : #(BOTH)
make.exe : mk.h mk1.h make.obj file.obj parsedir.obj macro.obj
$write sys$output "This is a number sign --> ##"
after macro expansion, the three dependencies will appear identical
and the two '#'s in the last line will turn into one '#'.
UNIX(tm) MAKE AND THIS ONE
They are NOT the same. Do not expect Unix makefiles to work with
this MAKE, even if you change the pathnames. There are some major
differences between this version and the standard Unix(tm) MAKE:
1. The Unix(tm) comment character is '#', VAX/VMS's is '!'.
2. The Unix(tm) macro-expansion character is '$'. While this would
have been easy to leave the same, the '$' character is used so
often in VAX/VMS command-lines that I thought it best to change
it to '#'.
3. Multiple root names are not allowed. Unix(tm) MAKE accepts lines
of the form:
name1 name2 : depend1 depend2
but this one doesn't.
4. There is no equivalent of double-colon ("::".)
5. There is no equivalent of .SUFFIXES, or the corresponding special
macros.
SAMPLE MAKEFILE
!
! VAX/VMS MAKE
! Landon Dyer
!
H = make.h
FILES = #H, make.c, macro.c, token.c, parsedir.c, file.c
DOCUMENTATION = distr.mem make.man makefile. make.com
make.exe : make.obj macro.obj token.obj parsedir.obj file.obj
$link make.obj, macro, token, parsedir, file
$purge
make.obj : make.c #H
$cc make.c
macro.obj : macro.c #H
$cc macro
token.obj : token.c #H
$cc token
parsedir.obj : parsedir.c #H
$cc parsedir
file.obj : file.c
$cc file
!
! Print files associated with MAKE
!
print :
$print make.man, #(FILES), make.com, makefile.
!
! Type out source to MAKE
!
type :
$type #(FILES), make.com, makefile.
!
! Make backup of source files.
!
BACKUP = [.bak]
backup :
$copy #(FILES) #(BACKUP)
$copy make.man, make.com, makefile. #(BACKUP)
!
! Collect MAKE into a distribution file.
!
collect :
$collect collect distr.mem make.man makefile make.com make.h -
make.c macro.c token.c parsedir.c file.c
AUTHOR
Landon Dyer G.DYER@SU-SCORE.ARPA
175 Calvert Dr. #F-211 BASHFL::DYER (Atari Coinop)
Cupertino, CA 95014
\e make.man
\b makefile
!
! MSDOS Make utility
! (compile with Lattice C version 2.0)
!
CLIB = \bin\lcs
COBJ = \bin\cs
H = make.h
FILES = #H make.c macro.c token.c parsedir.c file.c osdate.asm
DOCUMENTATION = readme make.man makefile
makeexe.exe : make.obj macro.obj token.obj parsedir.obj file.obj osdate.obj
link #(COBJ) make macro token parsedir file osdate,makeexe,,#(CLIB)
make.obj : make.c #H
lc1 make
lc2 make
macro.obj : macro.c #H
lc1 macro
lc2 macro
token.obj : token.c #H
lc1 token
lc2 token
parsedir.obj : parsedir.c #H
lc1 parsedir
lc2 parsedir
file.obj : file.c
lc1 file
lc2 file
osdate.obj : osdate.asm
masm osdate;
!
! Print files associated with MAKE
!
print :
print make.man #(FILES) makefile
!
! collect source and documentation files
!
collect :
collect -o make.col @make.lis
!
! copy to distribution disk (on A:)
!
distribution :
copy readme a:
copy make.man a:
copy makefile a:
copy make.bat a:
copy make.c a:
copy macro.c a:
copy token.c a:
copy parsedir.c a:
copy file.c a:
copy osdate.asm a:
copy cmake.bat a:
copy make.lis a:
copy makeexe.exe a:
\e makefile
\b make.bat
echo off
if exist make$$$$.bat del make$$$$.bat
makeexe %1 %2 %3 %4 %5 %6 %7 %8 %9
if exist make$$$$.bat make$$$$.bat
\e make.bat
\b make.c
#include <stdio.h>
#include <ctype.h>
#include "make.h"
/*
* MAKE - Maintain seperate source files
*
* SYNOPSIS
* MK [-f file] [-a] [-n] [-d] [name] ...
* f: use 'file' instead of default makefile
* a: assume all modules are obsolete (recompile everything)
* n: don't recompile, just list steps to recompile
* d: debugging (print tree, file info)
* name: module name to recompile
*
* 'secret' options (not to be used by humans):
* -ofile 'file' is the script file to write to
*
* AUTHOR
* Landon M. Dyer, Atari Inc.
*
*/
#define SCRIPTFILE "make$$$$.bat" /* (default) script-listing file */
#define INIT "~INIT" /* initialization macro */
#define DEINIT "~DEINIT" /* de-init macro */
#define BEFORE "~BEFORE" /* the per-root 'startup' method */
#define AFTER "~AFTER" /* the per-root 'wrapup' method */
char *mfiles[] = { /* default makefiles */
"makefile",
#ifdef VAXVMS
"[-]makefile",
"sys$login:makefile",
#endif
#ifdef MSDOS
"..\makefile",
#endif
""
};
MACRO *mroot = NULL; /* root of macro-list */
FILENODE *froot = NULL; /* root of filenode-list */
FILENODE *firstf = NULL; /* the very first filenode */
FILE *mkfp = NULL; /* script file */
char *modnames[MAXMODS]; /* module-names mentioned in commandline */
int modcount = 0; /* #of module-names */
int debug = 0; /* nonzero: turn on debugging */
int obsolete = 0; /* nonzero: every file should be recompiled */
int noscript = 0; /* nonzero: print methods on standard output */
char *scriptf = SCRIPTFILE; /* default script file */
DATE bigbang; /* a date, the very earliest possible */
DATE endoftime; /* a date, the very last possible */
main(argc, argv)
int argc;
char **argv;
{
int arg, i;
char *mfile = NULL;
DATE adate();
bigbang = adate(0, 0); /* init root dates */
endoftime = adate(~0, ~0);
for(arg = 1; arg < argc; ++arg)
if(*argv[arg] == '-') switch(tolower(argv[arg][1]))
{
case 'f':
if(++arg >= argc)
{
fprintf(stderr, "-f needs filename argument.\n")
;
return;
}
mfile = argv[arg];
break;
case 'a':
obsolete = 1;
break;
case 'n':
noscript = 1;
break;
case 'd':
debug = 1;
break;
case 'o':
scriptf = argv[arg] + 2;
break;
default:
fprintf(stderr, "Unknown switch: %c\n", argv[arg][1]);
break;
} else if(modcount < MAXMODS)
modnames[modcount++] = argv[arg];
else
{
fprintf(stderr, "Too many module names.\n");
return;
}
if(mfile != NULL)
{
if(fmake(mfile) == -1)
fprintf(stderr, "Cannot open makefile '%s'.\n", mfile);
} else {
for(i = 0; *mfiles[i]; ++i)
if(fmake(mfiles[i]) != -1) break;
if(!*mfiles[i])
fprintf(stderr, "Cannot open makefile.\n");
}
if(debug) prtree();
}
/*
* Construct dependency tree from the makefile 'fn'.
* Figure out what has to be recompiled, and write a script file to do that.
*/
fmake(fn)
char *fn;
{
FILE *fp;
if((fp = fopen(fn, "r")) == NULL) return -1;
fparse(fp);
determ();
fclose(fp);
return 0;
}
/*
* Parse the input file, defining macros and building the dependency tree.
*/
fparse(fp)
FILE *fp;
{
char ibuf[STRSIZ], ebuf[STRSIZ];
char *strp, *tok1, *tok2, *s;
FILENODE *lastf = NULL;
FILENODE *sf;
for(;;)
{
if(fgets(ibuf, STRSIZ, fp) == NULL) break;
mexpand(ibuf, ebuf, STRSIZ, MACCHAR);
escape(ebuf, COMCHAR);
/* clobber last newline in string */
s = ebuf + strlen(ebuf) - 1;
if(s >= ebuf && *s == '\n') *s = '\0';
if(*ebuf == '\t')
{
addmeth(lastf, ebuf+1);
continue;
}
strp = ebuf;
if((tok1 = token(&strp)) == NULL) continue;
if((tok2 = token(&strp)) != NULL)
if(!strcmp(tok2, DEFMAC))
{
if(*strp) defmac(tok1, strp);
else if(undefmac(tok1) < 0)
fprintf(stderr,
"Can't undefine macro '%s'\n", tok1);
continue;
}
else if(!strcmp(tok2, DEPEND))
{
addmeth(lastf, gmacro(AFTER));
lastf = filenode(tok1);
if(firstf == NULL) firstf = lastf;
lastf->fmake = NULL;
addmeth(lastf, gmacro(BEFORE));
lastf->fflag |= ROOTP;
while((tok1 = token(&strp)) != NULL)
addfile(lastf, tok1);
continue;
}
else addfile(lastf, tok2);
do {
addfile(lastf, tok1);
} while((tok1 = token(&strp)) != NULL);
}
addmeth(lastf, gmacro(AFTER));
}
/*
* Determine sequence of recompiles from the creation dates.
* If there is anything to recompile, then create a script file full of commands
.
*/
determ()
{
FILENODE *f;
int i;
char *m;
if(firstf == NULL) /* empty tree */
{
printf("No changes.\n");
return;
}
if(modcount == 0) examine(firstf, endoftime);
else for(i = 0; i < modcount; ++i)
{
if((f = gfile(modnames[i])) == NULL)
{
fprintf(stderr, "Can't find root '%s'.\n", modnames[i]);
continue;
}
if(f->fflag & ROOTP == 0)
{
fprintf(stderr, "'%s' is not a root!\n", f->fname);
continue;
}
examine(f, endoftime);
}
if(mkfp != NULL)
{
if((m = gmacro(DEINIT)) != NULL)
{
fputs(m, mkfp);
fputc('\n', mkfp);
}
fclose(mkfp);
} else printf("No changes.\n");
}
/*
* Examine filenode 'fnd' and see if it has to be recompiled.
* 'date' is the last-touched date of the node's father
* (or 'endoftime' if its a root file.)
* Root files with NO dependencies are assumed not to be up to date.
*/
examine(fnd, date)
FILENODE *fnd;
DATE date;
{
int rebuildp = 0;
NODE *n;
getdate(fnd);
if(fnd->fnode == NULL && fnd->fflag & ROOTP)
rebuildp = 1;
else for(n = fnd->fnode; n != NULL; n = n->nnext)
if(examine(n->nfile, fnd->fdate)) rebuildp = 1;
if(rebuildp) recomp(fnd);
if(obsolete || laterdt(fnd->fdate, date) >= 0)
rebuildp = 1;
return rebuildp;
}
/*
* Make sure a filenode gets recompiled.
*/
recomp(f)
FILENODE *f;
{
FILENODE *sf;
char *m;
if(mkfp == NULL)
{
if(noscript) mkfp = stdout;
else if((mkfp = fopen(scriptf, "w")) == NULL)
fprintf(stderr, "Cannot create: '%s'\n", scriptf);
if((m = gmacro(INIT)) != NULL)
{
fputs(m, mkfp);
fputc('\n', mkfp);
}
}
if(f->fflag & REBUILT) return;
if(f->fmake != NULL) fputs(f->fmake, mkfp);
f->fflag |= REBUILT;
}
/*
* Complain about being out of memory, and then die.
*/
allerr() {
fprintf(stderr, "Can't alloc -- no space left (I give up!)\n");
exit(1);
}
\e make.c
\b macro.c
#include <stdio.h>
#include "make.h"
/*
* Macro processing
*/
/*
* Perform macro substitution from 'orig' to 'dest'.
* Return number of macro substitutions made.
* A macro reference is in one of two forms:
* <MACCHAR>(macro-name)
* or <MACCHAR><single-character>
*
* "<MACCHAR><MACCHAR>" expands to a single '<MACCHAR>'
*/
mexpand(orig, dest, destsiz, macchar)
char *orig, *dest;
int destsiz;
char macchar;
{
char *s, *d, mname[STRSIZ];
int di, count;
MACRO *m;
di = count = 0;
for(s=orig; *s;)
if(*s == macchar)
{
if(*++s == macchar)
{
if(di < destsiz-1) dest[di++] = *s++;
continue;
}
if(!*s) break;
d = mname;
if(*s != '(') *d++ = *s++;
else
{
for(++s; *s && *s!=')';) *d++ = *s++;
if(*s != ')') puts("Missed matching ')'");
else ++s;
}
*d = 0;
if((d = gmacro(mname)) == NULL)
fprintf(stderr, "Undefined macro: %s\n", mname);
else
{
while(*d && di < (destsiz - 1))
dest[di++] = *d++;
++count;
}
} else if(di < destsiz-1)
dest[di++] = *s++;
dest[di]=0;
return count;
}
/*
* Define a macro.
* Give the macro called 'name' the string expansion 'def'.
* Old macro-names are superseded, NOT replaced.
* Return ERROR if can't define the macro.
*/
defmac(name, def)
char *name, *def;
{
MACRO *m;
if((m = (MACRO *)malloc(sizeof(MACRO))) == NULL) allerr();
if((m->mname = (char *)malloc(strlen(name)+1)) == NULL) allerr();
if((m->mvalue = (char *)malloc(strlen(def)+1)) == NULL) allerr();
strcpy(m->mname, name);
strcpy(m->mvalue, def);
m->mnext = mroot;
mroot = m;
}
/*
* undefmac - undefine a macro.
* Return 0 if macro was succesfully undefined, -1 if not found.
*/
undefmac(name)
char *name;
{
MACRO *m = mroot;
MACRO *prev = NULL;
while(m != NULL && strcmp(name, m->mname))
{
prev = m;
m = m->mnext;
}
if(m == NULL) return -1;
if(prev == NULL) mroot = m->mnext;
else prev->mnext = m->mnext;
free(m->mname);
free(m->mvalue);
free(m);
return 0;
}
/*
* Lookup a macro called 'name'.
* Return a pointer to its definition,
* or NULL if it does not exist.
*/
char *gmacro(name)
char *name;
{
MACRO *m;
for(m=mroot; m != NULL; m=m->mnext)
if(!strcmp(name, m->mname)) return m->mvalue;
return NULL;
}
\e macro.c
\b token.c
#include <stdio.h>
#include <ctype.h>
#include "make.h"
/*
* Get next token from the string. Return a pointer to it, or NULL.
* Adjust pointer to point to next part of string.
* The string is modified.
* A token consists of any number of non-white characters.
*/
char *token(strpp)
char **strpp;
{
char *s, *beg;
stripwh(strpp);
if(!**strpp) return NULL;
beg = s = *strpp;
while(*s && !isspace(*s)) ++s;
if(*s) *s++ = '\0';
*strpp = s;
return beg;
}
/*
* Parse character escape-sequences in a line of text.
* <EscChar><EscChar> = <EscChar>
* <EscChar>n = newline, and so on
* <EscChar><char> = <char>
* The string is truncated at the first non-escaped occurance of 'comchar'.
*/
escape(str, comchar)
char *str, comchar;
{
char *d, c;
for(d = str; *str && *str != comchar; ++str)
if(*str == ESCCHAR && *(str + 1)) switch((c = *++str))
{
case ESCCHAR:
*d++ = ESCCHAR;
break;
case 'n':
*d++ = '\n';
break;
case 'r':
*d++ = '\r';
break;
case 't':
*d++ = '\t';
break;
case 'b':
*d++ = '\b';
break;
case 'f':
*d++ = '\f';
break;
default:
*d++ = c;
break;
} else *d++ = *str;
*d++ = 0;
}
stripwh(strpp)
char **strpp;
{
char *s;
s = *strpp;
while(isspace(*s)) ++s;
return (*strpp = s);
}
\e token.c
\b parsedir.c
#include <stdio.h>
#include "make.h"
#ifdef VAXVMS
#include <rms.h>
#endif
/*
* Get a file's creation date.
*/
int getdate(f)
FILENODE *f;
{
if(f->fdate != NULL || filedate(f) != -1) return;
if(f->fflag & ROOTP == 0)
{
fprintf(stderr, "Can't get date for file '%s'\n", f->fname);
f->fdate = endoftime;
} else f->fdate = bigbang;
return;
}
#ifdef VAXVMS
/*
* filedate - return file's creation date (VAX/VMS only.)
* Returns -1 if file cannot be found, 0 if succesful.
*/
filedate(fnd)
FILENODE *fnd;
{
unsigned *datetime;
DATE adate();
struct FAB *fptr;
struct XABDAT *dptr;
fptr = malloc(sizeof(struct FAB)); /* allocate FAB and XABDAT */
dptr = malloc(sizeof(struct XABDAT));
if(fptr == NULL || dptr == NULL) allerr();
*fptr = cc$rms_fab; /* initialize FAB and XABDAT */
*dptr = cc$rms_xabdat;
fptr->fab$l_xab = (char *) dptr; /* FAB -> XABDAT */
fptr->fab$l_fna = fnd->fname; /* setup filename */
fptr->fab$b_fns = strlen(fnd->fname);
if(sys$open(fptr) != RMS$_NORMAL || /* open the file */
sys$display(fptr) != RMS$_NORMAL) /* get XABDAT info */
return -1;
datetime = &(dptr->xab$q_cdt); /* record 64-bit date */
fnd->fdate = adate(datetime[0], datetime[1]);
sys$close(fptr); /* close the file */
free(dptr); /* clean up and return */
free(fptr);
return 0;
}
#endif
#ifdef MSDOS
/*
* filedate - return file's creation date (MSDOS only.)
* Returns -1 if file cannot be found, 0 if successful
*/
filedate(fnd)
FILENODE *fnd;
{
unsigned date, time;
DATE adate();
if(osdate(fnd->fname, &time, &date) == -1) return -1;
fnd->fdate = adate(time, date);
}
#endif
/*
* laterdt - compare two dates.
* Return -1, 0 or 1 if date1 < date2, date1 == date2, or date1 > date2
*/
laterdt(date1, date2)
DATE date1, date2;
{
if(date1->ds_high > date2->ds_high ||
(date1->ds_high >= date2->ds_high &&
date1->ds_low > date2->ds_low)) return 1;
else if(date1->ds_high == date2->ds_high &&
date1->ds_low == date2->ds_low) return 0;
else return -1;
}
/*
* adate - allocate a date with the given time
*/
DATE adate(time1, time2)
unsigned time1, time2;
{
DATE d;
if((d = (DATE)malloc(sizeof(struct date_str))) == NULL) allerr();
d->ds_low = time1;
d->ds_high = time2;
return d;
}
\e parsedir.c
\b file.c
#include <stdio.h>
#include "make.h"
/*
* Return file-node for 'fname'.
* If it doesn't exist, then create one.
*/
FILENODE *filenode(fname)
char *fname;
{
FILENODE *f, *afnode(), *gfile();
if((f = gfile(fname)) == NULL)
f = afnode(fname);
return f;
}
/*
* Add a dependency to the node 'fnd'.
* 'fnd' will depend on 'fname'.
*/
addfile(fnd, fname)
FILENODE *fnd;
char *fname;
{
NODE *n;
FILENODE *f;
if(fnd == NULL) /* punt if no root file */
{
fprintf(stderr, "No current root, can't add dependency '%s'\n",
fname);
return;
}
f = filenode(fname);
if((n = (NODE *)malloc(sizeof(NODE))) == NULL) allerr();
n->nnext = fnd->fnode;
fnd->fnode = n;
n->nfile = f;
}
/*
* Add a line of method-text to the node 'fnode'.
*/
addmeth(fnode, methtext)
FILENODE *fnode;
char *methtext;
{
int len;
char *new;
if(fnode == NULL || methtext == NULL) return;
len = strlen(methtext) + 2;
if(fnode->fmake == NULL)
{
if((fnode->fmake = (char *)malloc(1)) == NULL) allerr();
*(fnode->fmake) = 0;
}
len += strlen(fnode->fmake);
/* Lattice C doesn't have 'realloc()', so this kludges around it: */
if((new = (char *)malloc(len)) == NULL) allerr();
strcpy(new, fnode->fmake);
free(fnode->fmake);
fnode->fmake = new;
strcat(fnode->fmake, methtext);
len = strlen(fnode->fmake);
if(len && fnode->fmake[len - 1] != '\n')
strcat(fnode->fmake, "\n");
}
/*
* Get a filenode for the file called 'fn'.
* Returns NULL if the node doesn't exist.
*/
FILENODE *gfile(fn)
char *fn;
{
FILENODE *f;
for(f = froot; f != NULL; f = f->fnext)
if(!strcmp(fn, f->fname)) return f;
return NULL;
}
/*
* Alloc space for a new file node.
*/
FILENODE *afnode(name)
char *name;
{
FILENODE *f;
for(f=froot; f; f=f->fnext)
if(!strcmp(name, f->fname)) return f;
if((f = (FILENODE *)malloc(sizeof(FILENODE))) == NULL) allerr();
if((f->fname = (char *)malloc(strlen(name)+1)) == NULL) allerr();
strcpy(f->fname, name);
f->fmake = NULL;
f->fnode = NULL;
f->fdate = NULL;
f->fflag = 0;
f->fnext = froot;
froot = f;
return f;
}
/*
* Print dependency tree.
*/
prtree()
{
FILENODE *f;
NODE *n;
for(f = froot; f != NULL; f = f->fnext)
{
printf("%s%s%s (%u, %u)\n",
f->fname,
(f->fflag & ROOTP) ? " (root)" : "",
(f->fflag & REBUILT) ? " (rebuilt)" : "",
(f->fdate != NULL) ? (f->fdate)->ds_high : 0,
(f->fdate != NULL) ? (f->fdate)->ds_low : 0);
if(f->fmake != NULL)
printf("%s", f->fmake);
for(n = f->fnode; n != NULL; n = n->nnext)
printf("\t%s\n", (n->nfile)->fname);
puts("");
}
}
\e file.c
\b osdate.asm
dos = 21h
arg1 = 4 ; lattice argument indexes
arg2 = arg1+2
arg3 = arg2+2
pgroup group prog
prog segment byte public 'prog'
public osdate
assume cs:pgroup
;
;------
; OSDATE - return file's creation-date (called from Lattice), or -1
; if can't find the file.
; Synopsis:
; int osdate(filename, time1, time2)
; char *filename;
; int *time1, *time2;
;
osdate proc near
push bp
mov bp,sp
;--- Open the file
mov dx,[bp+arg1]
xor al,al
mov ah,3dh
int dos
jc osd$err ; can't, so complain
;--- Get file's creation date and time
mov bx,ax ; get handle's date info
xor al,al
mov ah,57h
int dos
jc osd$cls ; "can't happen" (but close it)
;--- Install date/time info into caller's variables
mov si,[bp+arg2] ; *arg2 = time (least significant)
mov [si],cx
mov si,[bp+arg3] ; *arg3 = date (most significant)
mov [si],dx
;--- Close file & return (ok)
mov ah,3eh
int dos
xor ax,ax
pop bp
ret
;--- Close file & return error condition
osd$cls:
mov ah,3eh
int dos
osd$err:
mov ax,-1
pop bp
ret
osdate endp
prog ends
end
\e osdate.asm
\b cmake.bat
lc1 make
lc2 make
lc1 macro
lc2 macro
lc1 token
lc2 token
lc1 parsedir
lc2 parsedir
lc1 file
lc2 file
masm afind1st;
link c:c make macro token parsedir file afind1st,make;
\e cmake.bat
\b make.lis
readme make.man makefile make.bat make.c macro.c token.c
parsedir.c file.c osdate.asm cmake.bat make.lis
\e make.lis